package com.vclee.fast.utils.model;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import lombok.SneakyThrows;

import java.io.IOException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Bson extends HashMap<String,Object> implements Serializable {
    private final static ObjectMapper objectMapper = new ObjectMapper();
    static {
        // 日期处理设置
        SimpleModule module = new SimpleModule();
        module.addSerializer(LocalDate.class, new JsonSerializer<LocalDate>() {
            @Override
            public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeNumber(value.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli());
            }
        });

        module.addSerializer(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
            @Override
            public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
            }
        });

        module.addSerializer(Date.class, new JsonSerializer<Date>() {
            @Override
            public void serialize(Date value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
                gen.writeNumber(value.getTime());
            }
        });

        objectMapper.registerModule(module);
        // 设置多于字段忽略转换，不然会报错
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    }



    @SneakyThrows
    public static Bson create(Object object) {
        return toClazz(object, Bson.class);
    }

    @SneakyThrows
    public <T> T getObject(String key, Class<T> clazz) {
        return toClazz(this.get(key), clazz);
    }

    @SneakyThrows
    public Bson getBson(String key) {
        return getObject(key, Bson.class);
    }

    @SneakyThrows
    public <T> List<T> getArray(String key, Class<T> clazz) {
        List<?> list = toClazz(this.get(key), List.class);
        if(list==null || list.isEmpty()) return new ArrayList<>();
        return list.stream().map(item -> toClazz(item, clazz)).collect(Collectors.toList());
    }

    public List<Bson> getBsonArray(String key) {
        List<?> list = toClazz(this.get(key), List.class);
        if(list==null || list.isEmpty()) return new ArrayList<>();
        return list.stream().map(item -> toClazz(item, Bson.class)).collect(Collectors.toList());
    }

    public String getString(String key) {
        return toClazz(this.get(key), String.class);
    }

    @Override
    public String toString() {
        return toString(this);
    }

    public Boolean getBoolean(String key) {
        return toClazz(this.get(key), Boolean.class);
    }

    public Date getDate(String key) {
        return toClazz(this.get(key), Date.class);
    }

    public Integer getInteger(String key) {
        return toClazz(this.get(key), Integer.class);
    }

    public Long getLong(String key) {
        return toClazz(this.get(key), Long.class);
    }

    public Double getDouble(String key) {
        return toClazz(this.get(key), Double.class);
    }
    public BigDecimal getBigDecimal(String key) {
        return toClazz(this.get(key), BigDecimal.class);
    }

    public static String toString(Object object) {
        return toClazz(object, String.class);
    }

    /**
     * 对象转换
     */
    @SneakyThrows
    @SuppressWarnings("all")
    public static <T> T toClazz(Object object, Class<T> clazz) {
        if (object == null) {
            return object instanceof Collection? clazz.newInstance():clazz.cast(null);
        } else if (object instanceof Number) {
            return objectMapper.convertValue(String.valueOf(object), clazz);
        } else if (object instanceof String) {
            if (clazz == String.class) {
                return clazz.cast(object);
            }else {
                String data = (String) object;
                return (data == null || data.isEmpty())?clazz.cast(null):objectMapper.readValue(data, clazz);
            }
        } else {
            String source = objectMapper.writeValueAsString(object);
            if (clazz == String.class) return clazz.cast(source);
            return objectMapper.readValue(source, clazz);
        }
    }

    @SneakyThrows
    public static <T> List<T> toArray(Object object, Class<T> clazz) {
        Stream<?> stream = toClazz(object, List.class).stream();
        return stream.map(item -> toClazz(item, clazz)).collect(Collectors.toList());
    }

    public static List<Bson> toBsonArray(Object object) {
        Stream<?> stream = toClazz(object, List.class).stream();
        return stream.map(item -> toClazz(item, Bson.class)).collect(Collectors.toList());
    }
}
